use std::io;

use serde::{de::DeserializeOwned, Serialize};

use crate::prelude::*;

/// Wrapper to marshal something serde-able in CBOR form.
// TODO make a version of this that doesn't require a clone to instantiate, maybe using Cow
pub struct SerdeCbor<T> {
    v: T,
}

impl<T> SerdeCbor<T> {
    pub fn new(v: T) -> Self {
        Self { v }
    }

    pub fn inner(&self) -> &T {
        &self.v
    }

    pub fn inner_mut(&mut self) -> &mut T {
        &mut self.v
    }

    pub fn into_inner(self) -> T {
        self.v
    }
}

impl<T: Serialize> Encode for SerdeCbor<T> {
    fn encode<E: Encoder>(&self, enc: &mut E) -> Result<(), CodecError> {
        // TODO rework types to avoid alloc if possible
        let mut buf = Vec::new();
        ciborium::into_writer(&self.v, &mut buf).map_err(|e| CodecError::from_tostr(&e))?;
        enc.push_var_buf(&buf)
    }
}

impl<T: DeserializeOwned> Decode for SerdeCbor<T> {
    fn decode<'de, D: Decoder<'de>>(dec: &mut D) -> Result<Self, CodecError> {
        let buf = dec.take_var_buf()?;
        let mut cur = io::Cursor::new(buf);
        let v = ciborium::from_reader(&mut cur).map_err(|e| CodecError::from_tostr(&e))?;
        Ok(Self { v })
    }
}
